package com.fm.magic.net.proxy;

import com.fm.base.annotation.ThreadSafe;
import com.fm.base.database.DBClient;
import com.fm.base.database.DBCursor;
import com.fm.base.database.DBDomainService;
import com.fm.base.database.domain.DBDocument;
import com.fm.base.log.Loggers;
import com.fm.base.net.proxy.HttpProxy;
import com.fm.base.struct.BasicHashSet;
import com.fm.commons.util.struct.BasicCounter;
import com.fm.magic.Factory;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@ThreadSafe
public class HttpProxyPool implements ProxyPool<HttpProxy> {
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	private List<HttpProxy> proxies = new ArrayList<>();
	private BasicCounter<HttpProxy> success = new BasicCounter<>();
	private BasicCounter<HttpProxy> fail = new BasicCounter<>();
	private int limit;
	private float sucessRate;

	public HttpProxyPool() {
		this(3, 0.0f);
	}

	public HttpProxyPool(int limit, float sucessRate) {
		this.limit = limit;
		this.sucessRate = sucessRate;
		try(DBClient client = Factory.getLocalProxiesDB()) {
			DBDomainService<DBDocument> domain = client.getDomainService("proxies", DBDocument.class);
			try(DBCursor<DBDocument> cursor = domain.cursor(null).fields(new BasicHashSet<String>().append("_id").append("type")).getCursor()) {
				while (cursor.hasNext()) {
					DBDocument doc = cursor.next();
					String id = doc.getString("_id");
					proxies.add(new HttpProxy(
						id.substring(0, id.indexOf(':')),
						Integer.parseInt(id.substring(id.indexOf(':') + 1)),
						HttpProxy.Type.valueOf(doc.getString("type"))));
				}
			}
		}
	}

	@Override
	public HttpProxy get() {
		try {
			lock.readLock().lock();
			return proxies.get(new Random().nextInt(proxies.size()));
		} finally {
			lock.readLock().unlock();
		}
	}

	public void doSuccess(HttpProxy proxy) {
		success.add(proxy);
	}

	public void doFail(HttpProxy proxy) {
		fail.add(proxy);
		AtomicInteger s = success.get(proxy);
		AtomicInteger f = fail.get(proxy);
		int total = (s == null ? 0 : s.get()) + (f == null ? 0 : f.get());
		if (total >= limit && (s == null ? 0 : s.get()) / (float) total <= sucessRate) {
			try {
				lock.writeLock().lock();
				proxies.remove(proxy);
				Loggers.CONSOLE.info("remove proxy " + proxy + ", remaining:" + proxies.size());
			} finally {
				lock.writeLock().unlock();
			}
		}
	}

	@Override
	public boolean add(HttpProxy proxy) {
		try {
			lock.writeLock().lock();
			return proxies.add(proxy);
		} finally {
			lock.writeLock().unlock();
		}
	}

	@Override
	public boolean remove(HttpProxy proxy) {
		try {
			lock.writeLock().lock();
			return proxies.remove(proxy);
		} finally {
			lock.writeLock().unlock();
		}
	}

	@Override
	public int getRemain() {
		try {
			lock.readLock().lock();
			return proxies.size();
		} finally {
			lock.readLock().unlock();
		}
	}
}
